﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;

namespace Rainbird.Tools.COMInterop
{
    /// <summary>
    /// Ermöglicht .NET-Anwendungen direkten Zugriff auf die Running Object Table (Tabelle mit allen momentan laufenden COM-Objekte)
    /// </summary>
    public class RunningObjectTable
    {
        /// <summary>
        /// Privater Standardkonstruktor.
        /// </summary>
        private RunningObjectTable()
        { }

        // Win32-API-Aufruf zum lesen der ROT
        [DllImport("ole32.dll")]
        private static extern int GetRunningObjectTable(uint reserved, out IRunningObjectTable pprot);

        // Win32-API-Aufruf zum erstellen von Bindungen
        [DllImport("ole32.dll")]
        private static extern int CreateBindCtx(uint reserved, out IBindCtx pctx);

        /// <summary>
        /// Gibt einen Verweis auf eine laufendes COM-Objekt anhand ihres Anzeigenamens zurück.
        /// </summary>
        /// <param name="objectDisplayName">Anzeigename einer COM-Instanz</param>
        /// <returns>Verweis auf COM-Objekt, oder null, wenn kein COM-Objekt mit dem angegbenen Namen läuft</returns>
        public static object GetRunningCOMObjectByName(string objectDisplayName)
        {
            // ROT-Schnittstelle
            IRunningObjectTable runningObjectTable = null;

            // Moniker-Auflistung
            IEnumMoniker monikerList = null;

            try
            {
                // Running Object Table abfragen und nichts zurückgeben, wenn keine COM-Objekte laufen
                if (GetRunningObjectTable(0, out runningObjectTable) != 0 || runningObjectTable == null) return null;

                // Moniker abfragen
                runningObjectTable.EnumRunning(out monikerList);

                // An den Anfang der Auflistung springen
                monikerList.Reset();

                // Array für Moniker-Abfrage erzeugen
                IMoniker[] monikerContainer = new IMoniker[1];

                // Zeiger auf die Anzahl der tatsächlich abgefragten Moniker erzeugen
                IntPtr pointerFetchedMonikers = IntPtr.Zero;

                // Alle Moniker durchlaufen
                while (monikerList.Next(1, monikerContainer, pointerFetchedMonikers) == 0)
                {
                    // Objekt für Bindungsinformationen
                    IBindCtx bindInfo;

                    // Variable für den Anzeigenamen des aktuellen COM-Objekts
                    string displayName;

                    // Bindungsobjekt erzeugen
                    CreateBindCtx(0, out bindInfo);

                    // Anzeigename des COM-Objekts über den Moniker abfragen
                    monikerContainer[0].GetDisplayName(bindInfo, null, out displayName);

                    // Bindungsobjekt entsorgen
                    Marshal.ReleaseComObject(bindInfo);

                    // Wenn der Anzeigename mit dem gesuchten übereinstimmt ...
                    if (displayName.IndexOf(objectDisplayName) != -1)
                    {
                        // Variable für COM-Objekt
                        object comInstance;

                        // COM-Objekt über den Anzeigenamen abrfragen
                        runningObjectTable.GetObject(monikerContainer[0], out comInstance);

                        // COM-Objekt zurückgeben
                        return comInstance;
                    }
                }
            }
            catch
            {
                // Nichts zurückgeben
                return null;
            }
            finally
            {
                // Ggf. COM-Verweise entsorgen
                if (runningObjectTable != null) Marshal.ReleaseComObject(runningObjectTable);
                if (monikerList != null) Marshal.ReleaseComObject(monikerList);
            }
            // Nichts zurückgeben
            return null;
        }

        /// <summary>
        /// Gibt eine Liste mit Anzeigenamen aller momentan laufenden COM-Objekte zurück.
        /// </summary>
        /// <returns>Liste mit Anzeigenamen</returns>
        public static IList<string> GetRunningCOMObjectNames()
        {
            // Auflistung der Anzeigenamen erzeugen
            IList<string> result = new List<string>();

            // Informationsobjekt der laufenden COM-Instanzen
            IRunningObjectTable runningObjectTable = null;

            // Moniker-Auflistung
            IEnumMoniker monikerList = null;

            try
            {
                // Running Object Table abfragen und nichts zurückgeben, wenn keine COM-Objekte laufen
                if (GetRunningObjectTable(0, out runningObjectTable) != 0 || runningObjectTable == null) return null;

                // Moniker abfragen
                runningObjectTable.EnumRunning(out monikerList);

                // An den Anfang der Auflistung springen
                monikerList.Reset();

                // Array für Moniker-Abfrage erzeugen
                IMoniker[] monikerContainer = new IMoniker[1];

                // Zeiger auf die Anzahl der tatsächlich abgefragten Moniker erzeugen
                IntPtr pointerFetchedMonikers = IntPtr.Zero;

                // Alle Moniker durchlaufen
                while (monikerList.Next(1, monikerContainer, pointerFetchedMonikers) == 0)
                {
                    // Objekt für Bindungsinformationen
                    IBindCtx bindInfo;

                    // Variable für den Anzeigenamen des aktuellen COM-Objekts
                    string displayName;

                    // Bindungsobjekt erzeugen
                    CreateBindCtx(0, out bindInfo);

                    // Anzeigename des COM-Objekts über den Moniker abfragen
                    monikerContainer[0].GetDisplayName(bindInfo, null, out displayName);

                    // Bindungsobjekt entsorgen
                    Marshal.ReleaseComObject(bindInfo);

                    // Anzeigenamen der Auflistung zufügen
                    result.Add(displayName);
                }
                // Auflistung zurückgeben
                return result;
            }
            catch
            {
                // Nichts zurückgeben
                return null;
            }
            finally
            {
                // Ggf. COM-Verweise entsorgen
                if (runningObjectTable != null) Marshal.ReleaseComObject(runningObjectTable);
                if (monikerList != null) Marshal.ReleaseComObject(monikerList);
            }
        }
    }
}